#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctime>
#include "task.hpp"

#define TIME 5

//return value
enum
{
    USAGE_ERROR = 1,
    PROCESS_NUM_ERROR, 
    PIPE_ERROR,
    SELECT_CHANNEL_ERROR,
    SELEDT_TASK_ERROR
};

void usage()
{
    std::cout << "Usage: ./processpool [process_num]" << std::endl;
}

class channel
{
public:
    channel(std::string name, int rfd, int wfd, pid_t child_id)
    : _name(name), _rfd(rfd), _wfd(wfd), _child_id(child_id)
    {}

    const std::string& get_name()
    {
        return _name;
    }

    int get_rfd()
    {
        return _rfd;
    }

    int get_wfd()
    {
        return _wfd;
    }

    pid_t get_child_id()
    {
        return _child_id;
    }

    void close_fd()
    {
        close(_wfd);
    }
private:
    const std::string _name;
    const int _rfd;
    const int _wfd;
    const pid_t _child_id;
};

class processpool
{
public:
    processpool(int process_num)
    : _process_num(process_num)
    {}

    //创建管道
    int create_channel()
    {
        std::vector<int> fd;
        for (int i = 0; i < _process_num; i++)
        {
            int pipefd[2] = {0};
            int ret = pipe(pipefd);
            if (-1 == ret)
            {
                perror("pipe:");
                return PIPE_ERROR;
            }

            pid_t id = fork();
            if (id == 0)
            {
                //child: read
                close(pipefd[1]);
                dup2(pipefd[0], 0);

                if (!fd.empty())
                {
                    for (auto k : fd)
                        close(k);
                }
                
                //work
                worker(i);

                std::cout << "channel " << i << " pid: " << getpid() <<  " exit" << std::endl;
                exit(-1);
            }

            //father: write
            close(pipefd[0]);

            std::string name = std::string("channel: ") + std::to_string(i);
            channel c = channel(name, pipefd[0], pipefd[1], id);
            _channels.push_back(c);

            fd.push_back(pipefd[1]);
        }

        return 0;
    }    

    int select_channel()
    {
        static int c = 0;
        int ret = c;
        c = (++c) % _process_num;
        return ret;
    }

    int select_task()
    {
        return rand() % 3;
    }

    int control_child()
    {
        int cnt = TIME;
        while(cnt--)
        {
            //选择一个进程(管道)，一个任务
            int channel_index = select_channel();
            int task_index = select_task();
            if (channel_index >= _process_num)
            {
                std::cout << "select channel error" << std::endl;
                return SELECT_CHANNEL_ERROR;
            }
            if (task_index >= 3)
            {
                std::cout << "select task error" << std::endl;
                return SELEDT_TASK_ERROR;
            }

            //发送任务
            int wfd = _channels[channel_index].get_wfd();
            write(wfd, &task_index, sizeof(int));

            sleep(2);
        }

        return 0;
    }

    void PrintDebug()
    {
        for (auto channel: _channels)
        {
            std::cout << channel.get_name() << ": rfd: " << channel.get_rfd() << ": child_id: " << channel.get_child_id() << std::endl;
        }
    }
    
    void clean_wait()
    {
        //关闭所有的写端
        for (auto channel : _channels)
        {
            channel.close_fd();

            pid_t id = waitpid(channel.get_child_id(), nullptr, 0);
            if (-1 == id)
            {
                perror("waitpid:");
            }
            if(id == channel.get_child_id())
            {
                std::cout << "wait child: " << channel.get_child_id() << " success\n" << std::endl;
            }
        }
    }
private:
    const int _process_num;
    std::vector<channel> _channels;
};

//  ./process num
int main(int argc, char* argv[])
{
    if (1 == argc)
    {
        usage();
        return USAGE_ERROR;
    }

    int process_num = std::stoi(argv[1]);
    
    if (process_num <= 0)
    {
        std::cout << "process_num should be grearter than 0" << std::endl;
        return PROCESS_NUM_ERROR;
    }
    
    srand((unsigned int)time(nullptr));

    std::cout << "process nums: " << process_num << std::endl;

    //创建进程池
    processpool* processpool_1 = new processpool(process_num);

    //创建管道
    int ret = processpool_1->create_channel();   
    if (0 != ret)
    {
        return ret;
    }
    std::cout << "create channels complete" << std::endl; 

    //工作
    ret = processpool_1->control_child();
    if (0 != ret)
    {
        return ret;
    }

    //回收 
    processpool_1->clean_wait();

    //调试
    // processpool_1->PrintDebug();
    // std::cout << "Debug complete" << std::endl;

    // sleep(100);

    return 0;
}